﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Clueless
{
    public class Game
    {
        private Board board;
        private CardDeck cardDeck;
        private Card solutionWeapon;
        private Card solutionCharacter;
        private Card solutionRoom;
        private CardDeck characterDeck;
        private PlayerList players;
        private Network networkOps;
        private Form1 userInterface;
        private Player me;
        ChooseCharacter frmChoose;
        Thread chooseThread;
        endOfTurn frmEndTurn;
        suggestion frmSuggest;
        suggestionMade frmSuggestionMade;
        suggestionDisproved frmSuggestionDisproved;
        accusation frmAccuse;
        gameOver frmGameOver;

        private bool isMyTurn = false;

        public Game(Form1 ui)
        {
            userInterface = ui; // Set the user interface
            userInterface.Output("Game created");
            
            // Setup the character choice form
            frmChoose = new ChooseCharacter();
            frmChoose.CharacterSelected += CharacterSelected;
            frmChoose.Show();
            frmChoose.Hide();

            frmEndTurn = new endOfTurn();
            frmEndTurn.OptionSelected += CompleteEndTurnAction;
            frmEndTurn.Show();
            frmEndTurn.Hide();

            frmSuggest = new suggestion();
            frmSuggest.CancelSelected += CancelSuggestion;
            frmSuggest.SuggestSelected += Suggest;
            frmSuggest.Show();
            frmSuggest.Hide();


            frmSuggestionMade = new suggestionMade();
            frmSuggestionMade.SuggestionWasDisproved += Disprove;
            frmSuggestionMade.Show();
            frmSuggestionMade.Hide();

            frmSuggestionDisproved = new suggestionDisproved();
            frmSuggestionDisproved.Show();
            frmSuggestionDisproved.Hide();

            frmAccuse = new accusation();
            frmAccuse.AccuseSelected += AccuseSelected;
            frmAccuse.Show();
            frmAccuse.Hide();

            frmGameOver = new gameOver();
            frmGameOver.Show();
            frmGameOver.Hide();

            me = new Player();

            // Generate the card deck
            userInterface.Output("Generating main deck...");
            cardDeck = new CardDeck(true);

            #region CardCreationDebug
            userInterface.Output("Deck generated.  Total: " + cardDeck.Cards.Count);
            foreach (Card c in cardDeck.Cards)
            {
                userInterface.CardOutput(c.Name + "  ::  " + c.CardType);
            }
            #endregion

            // Shuffle that shit!  -- DEPRICATED FROM CLIENT
            /*
            cardDeck.Shuffle();

            #region CardShuffleDebug
            userInterface.Output("Shuffled the deck!  Total: " + cardDeck.Cards.Count);
            userInterface.ResetCardOutput();
            foreach (Card c in cardDeck.Cards)
            {
                userInterface.CardOutput(c.Name + "  ::  " + c.CardType);
            }
            #endregion
            */

            userInterface.Output("Generating Board...");
            board = new Board();
            #region BoardCreationDebug
            userInterface.ResetNeighborListOutput();
            userInterface.RoomOutput(board.Rooms.First.Value);
            userInterface.NeighborOutput(board.Rooms.First.Value);
            #endregion


            characterDeck = new CardDeck(false);
            characterDeck.GenerateCharacters();
            foreach (Card c in characterDeck.Cards)
            {
                userInterface.Output(c.Name + "  ::  " + c.CardType);
            }

            // Get winning cards  -- DEPRICATE FROM CLIENT
            /*
            userInterface.Output("Getting solution...");
            bool gotChar = false, gotRoom = false, gotWeap = false;
            foreach (Card c in cardDeck.Cards)
            {
                if (c.CardType == CardType.Character && !gotChar)
                {
                    solutionCharacter = c;
                    gotChar = true;
                }
                else if (c.CardType == CardType.Room && !gotRoom)
                {
                    solutionRoom = c;
                    gotRoom = true;
                }
                else if (c.CardType == CardType.Weapon && !gotWeap)
                {
                    solutionWeapon = c;
                    gotWeap = true;
                }

                if (gotChar && gotRoom && gotWeap)
                {
                    break;
                }
            }

            cardDeck.RemoveSolution(solutionCharacter, solutionRoom, solutionWeapon);
            
            #region SolutionDebug
            userInterface.Output("Character: " + solutionCharacter.Name);
            userInterface.Output("Weapon: " + solutionWeapon.Name);
            userInterface.Output("Room: " + solutionRoom.Name);
            userInterface.ResetCardOutput();
            foreach (Card c in cardDeck.Cards)
            {
                userInterface.CardOutput(c.Name + "  ::  " + c.CardType);
            }
            #endregion
            */

            //Init the list of players..
            players = new PlayerList();

            
        }

        internal void StartNetwork(string ip, string port)
        {
            networkOps = new Network(ip, port);
            networkOps.AddCardMessage += AddCard;
            networkOps.CharacterSelectMessage += SelectCharacter;
            networkOps.SyncCharacterMessage += SyncCharacter;
            networkOps.StartTurn += StartTurn;
            networkOps.SuggestionMade += SuggestionMade;
            networkOps.DisproveMade += DisproveMade;
            networkOps.AccusationResponse += AccusationResponseDetected;
            networkOps.PlayerLost += PlayerLost;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void StartTurn(object sender, StartTurnEventArgs e)
        {
            players.SetCurrentPlayer(e.character);
            userInterface.ListPlayers(players);

            userInterface.SetTurn((players.GetCurrentPlayer().Character.Name == me.Character.Name));

            

            if (players.GetCurrentPlayer().Character.Name == me.Character.Name)
            {
                if (me.Active)
                {
                    userInterface.SetSuggestButtonVisibility(me.CanSuggestAtStart);
                    me.CanSuggestAtStart = false;

                    isMyTurn = true;
                    userInterface.ClearAllHighlights();
                    userInterface.EnableAccuse(true);
                    foreach (Room r in me.CurrentRoom.NeighboringRooms)
                    {
                        userInterface.HighlightRoom(r);
                    }

                    if (!CanMoveAtStart())
                    {
                        frmEndTurn.Invoke((MethodInvoker)delegate()
                        {
                            frmEndTurn.IsHall((me.CurrentRoom.RoomType == RoomType.Hallway), CanMove());
                            frmEndTurn.Show();
                        });
                        
                    }
                }
                else
                {
                    EndTurn();
                }
            }
            else
            {
                isMyTurn = false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void EndTurn()
        {
            me.CanSuggestAtStart = false;
            userInterface.SetSuggestButtonVisibility(false);
            userInterface.ClearAllHighlights();
            userInterface.EnableAccuse(false);
            networkOps.SendMessage("", null, MessageType.Turn);
        }


        /// <summary>
        /// Requests the user to select a character by opening the character selection form
        /// Binds the character selected event to <see cref="CharacterSelected">CharacterSelected</see>
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SelectCharacter(object sender, CharacterSelectEventArgs e)
        {
            LinkedList<String> charactersAvailable = new LinkedList<string>(e.names.Split(';'));

            

            // Work some magic to make the program not shit itself on the wrong thread... ughh
            frmChoose.Invoke((MethodInvoker)delegate() {
                frmChoose.SetCharacterList(charactersAvailable);
                frmChoose.Show();
                });
        }


        /// <summary>
        /// Runs when user has selected a character from the character selection form
        /// Also set the starting room!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CharacterSelected(object sender, CharacterSelectedEventArgs e)
        {
            me.Character = characterDeck.GetCard(e.Name);
            userInterface.Output("I am " + me.Character.Name);


            switch (me.Character.Name)
            {
                case "Colonel Mustard":
                    me.CurrentRoom = Board.GetRoom("Lounge To Dining Room");
                    me.PlayerColor = Color.Yellow;
                    break;
                case "Professor Plum":
                    me.CurrentRoom = Board.GetRoom("Library To Study");
                    me.PlayerColor = Color.Purple;
                    break;
                case "Mrs. Peacock":
                    me.CurrentRoom = Board.GetRoom("Conservatory To Library");
                    me.PlayerColor = Color.Blue;
                    break;
                case "Miss Scarlet":
                    me.CurrentRoom = Board.GetRoom("Hall To Lounge");
                    me.PlayerColor = Color.Red;
                    break;
                case "Mr. Green":
                    me.CurrentRoom = Board.GetRoom("Ballroom To Conservatory");
                    me.PlayerColor = Color.Green;
                    break;
                case "Mrs. White":
                    me.CurrentRoom = Board.GetRoom("Kitchen To Ballroom");
                    me.PlayerColor = Color.White;
                    break;
                default:
                    throw new Exception("Failed to detemine who the character was. " + me.Character.Name);
                    break;
            }

            players.AddPlayer(me);
            networkOps.SendMessage(me.Character.Name, null, MessageType.ChooseCharacter);

            userInterface.SetPlayerName(me);
        }


        /// <summary>
        /// Adds the card sent from the network to the hand
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddCard(object sender, AddCardEventArgs e)
        {
            me.Cards.AddCard(cardDeck.GetCard(e.cardName));
            userInterface.Output("Adding card: " + e.cardName);

            userInterface.ResetCardOutput();
            foreach (Card c in me.Cards.Cards)
            {
                userInterface.CardOutput(c.Name + "  ::  " + c.CardType);
            }

            userInterface.ListCards(me.Cards);
        }

        /// <summary>
        /// Add a character to the character list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void SyncCharacter(object sender, SyncCharacterArgs e)
        {
            // step through each character in the list ... ; delim

            foreach (String client in e.Message.Split(';'))
            {
                String name = client.Split(':')[0];
                String room = client.Split(':')[1];

                // check if name exists..
                if (players.PlayerExists(name))
                {
                    if (me.Character != null && me.CurrentRoom != null)
                    {
                        if (me.Character.Name == name && me.CurrentRoom.Name != room)
                        {
                            me.CanSuggestAtStart = true;
                        }
                    }
                    // yes, update the position
                    players.GetPlayer(name).CurrentRoom = board.GetRoom(room);
                }
                else
                {   
                    // no, add the player
                    Player tempPlayer = new Player(cardDeck.GetCard(name), board.GetRoom(room));

                    switch (tempPlayer.Character.Name)
                    {
                        case "Colonel Mustard":
                            tempPlayer.PlayerColor = Color.Yellow;
                            break;
                        case "Professor Plum":
                            tempPlayer.PlayerColor = Color.Purple;
                            break;
                        case "Mrs. Peacock":
                            tempPlayer.PlayerColor = Color.Blue;
                            break;
                        case "Miss Scarlet":
                            tempPlayer.PlayerColor = Color.Red;
                            break;
                        case "Mr. Green":
                            tempPlayer.PlayerColor = Color.Green;
                            break;
                        case "Mrs. White":
                            tempPlayer.PlayerColor = Color.White;
                            break;
                        default:
                            throw new Exception("Failed to detemine who the character was. " + me.Character.Name);
                            break;
                    }

                    players.AddPlayer(tempPlayer); 
                }
            }


            userInterface.Output("New Player Information!");
            RenderPlayers();

            userInterface.ListPlayers(players);
        }


        private delegate void SuggestionDelegate(object sender, SuggestionMadeEventArgs e);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void SuggestionMade(object sender, SuggestionMadeEventArgs e)
        {
            if (frmSuggestionMade.InvokeRequired)
                frmSuggestionMade.Invoke(new SuggestionDelegate(SuggestionMade), sender, e);
            else
            {
                Console.WriteLine("suggestion made");
                frmSuggestionMade.SetSuggestion(e.message.Split(':')[0], e.message.Split(':')[1], e.message.Split(':')[2], e.suggester, me.Cards, me);
                frmSuggestionMade.Show();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DisproveMade(object sender, SuggestionMadeEventArgs e)
        {
            if (e.message.Split(':')[4] == "none")
            {
                // No one disproved your suggestion
                

                if (e.suggester == me.Character.Name)
                {
                    EndTurn();
                    frmSuggestionDisproved.SetDisproval(e.message.Split(':')[0], e.message.Split(':')[2], e.message.Split(':')[1], e.message.Split(':')[4], e.suggester, e.message.Split(':')[3], true, me.Character.Name);
                }
                else
                {
                    frmSuggestionDisproved.SetDisproval(e.message.Split(':')[0], e.message.Split(':')[2], e.message.Split(':')[1], e.message.Split(':')[4], e.suggester, e.message.Split(':')[3], false, me.Character.Name);
                }
            }
            else if (e.suggester != me.Character.Name)
            {
                // Another player's suggestion was disproved!
                frmSuggestionDisproved.SetDisproval(e.message.Split(':')[0], e.message.Split(':')[2], e.message.Split(':')[1], e.message.Split(':')[4], e.suggester, e.message.Split(':')[3], false, me.Character.Name);
            }
            else
            {
                // End my turn
                EndTurn();
                frmSuggestionDisproved.SetDisproval(e.message.Split(':')[0], e.message.Split(':')[2], e.message.Split(':')[1], e.message.Split(':')[4], e.suggester, e.message.Split(':')[3], true, me.Character.Name);
                // Your suggestion was disproved
                
            }

            frmSuggestionDisproved.Invoke((MethodInvoker)delegate()
            {
                frmSuggestionDisproved.Show();
            });
            

        }

        /// <summary>
        /// 
        /// </summary>
        public Board Board
        {
            get
            {
                return board;
            }
            set
            {
                board = value;
            }
        }

        public CardDeck CardDeck
        {
            get
            {
                return cardDeck;
            }
            set
            {
                cardDeck = value;
            }
        }

        public Network NetworkOps
        {
            get
            {
                return networkOps;
            }
            set
            {
                networkOps = value;
            }
        }

        public PlayerList PlayerList
        {
            get
            {
                return players;
            }
            set
            {
                players = value;
            }
        }

        /// <summary>
        /// Shuffles the cards (DEPRICATED ON CLIENT)
        /// </summary>
        public void Reshuffle()
        {
            cardDeck.Shuffle();
            userInterface.Output("Shuffled the deck!  Total: " + cardDeck.Cards.Count);
            userInterface.ResetCardOutput();
            foreach (Card c in cardDeck.Cards)
            {
                userInterface.CardOutput(c.Name + "  :: " + c.CardType);
            }
        }

        public void Start()
        {
            userInterface.Output("Game started");
        }

        public void End()
        {
            try
            {

                networkOps.KillConnection();
            }
            catch (Exception e)
            { }
            
        }

        public void Render()
        {
            throw new System.NotImplementedException();
        }

        public void DealCards()
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Attempts to validate then move to the room upon success
        /// </summary>
        /// <param name="roomName"></param>
        public void AttemptMove(string roomName)
        {
            Console.WriteLine("Attempt - " + roomName);
            if (isMyTurn)
            {
                Console.WriteLine("isMyTurn - " + roomName);
                if (IsValidMove(roomName))
                {
                    Console.WriteLine("IsValid - " + roomName);
                    MoveTo(roomName);

                    // Prompt for end of turn?? **********************************************************************************************
                }
            }
        }

        /// <summary>
        /// Validates the move
        /// - Is the room a neighbor to the current room
        /// </summary>
        /// <param name="roomName"></param>
        /// <returns></returns>
        public bool IsValidMove(String roomName)
        {
            if (me.CurrentRoom != null)
            {
                if (Board.GetRoom(roomName).RoomType == RoomType.Hallway && Board.GetRoom(roomName).isOccupied())
                {
                    return false;
                }

                return me.CurrentRoom.IsNeighbor(Board.GetRoom(roomName));
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Moves the player
        /// </summary>
        /// <param name="roomName"></param>
        public void MoveTo(String roomName)
        {
            Console.WriteLine("MoveTo - " + roomName);
            me.CurrentRoom = Board.GetRoom(roomName);
            isMyTurn = false;
            RenderPlayers();

            networkOps.SendMessage(me.CurrentRoom.Name, me.Character.Name, MessageType.Move);

            frmEndTurn.Invoke((MethodInvoker)delegate()
            {
                frmEndTurn.IsHall((me.CurrentRoom.RoomType == RoomType.Hallway), CanMove());
                frmEndTurn.Show();
            });

        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool CanMove()
        {
            
            if (me.CurrentRoom.isHall())
            {
                return false;
            }
            else
            {
                int count = 0;
                foreach (Room r in me.CurrentRoom.NeighboringRooms)
                {
                    if (r.isHall() && r.isOccupied())
                    {

                    }
                    else
                    {
                        count++;
                    }
                }

                return (count > 0);
            }
        
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool CanMoveAtStart()
        {

                int count = 0;
                foreach (Room r in me.CurrentRoom.NeighboringRooms)
                {
                    if (r.isHall() && r.isOccupied())
                    {

                    }
                    else
                    {
                        count++;
                    }
                }

                return (count > 0);

        }

        /// <summary>
        /// 
        /// </summary>
        public void SetOccupancy()
        {
            if (Board != null && players != null)
            {
                foreach (Room r in Board.Rooms)
                {
                    r.Occupied = false;
                }

                foreach (Player p in players.Players)
                {
                    Board.GetRoom(p.CurrentRoom.Name).Occupied = true;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void RenderPlayers()
        {
            SetOccupancy();

            userInterface.ClearAllRooms();
            foreach (Player p in players.Players)
            {
                if (p.Active)
                {
                    userInterface.Output(p.Character.Name + " @ " + p.CurrentRoom.Name);
                    userInterface.AddToRoom(p);
                }

            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CancelSuggestion(object sender, EventArgs e)
        {
            frmEndTurn.Invoke((MethodInvoker)delegate()
            {
                frmEndTurn.Show();
            });
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Suggest(object sender, SuggestionEventArgs e)
        {
            if (players.PlayerExists(e.character))
            {
                networkOps.SendMessage(me.CurrentRoom.Name, e.character, MessageType.Move);
                //Thread.Sleep(300);
            }
            networkOps.SendMessage(e.character + ":" + e.room + ":" + e.weapon, me.Character.Name, MessageType.Suggest);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Disprove(object sender, SuggestionMadeEventArgs e)
        {
            Console.WriteLine("Disproved: " + e.message);


            networkOps.SendMessage(e.message, me.Character.Name, MessageType.SuggestResponse);

        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CompleteEndTurnAction(object sender, OptionSelectedEventArgs e)
        {
            switch (e.option)
            {
                case "End Turn":
                    EndTurn();
                    break;
                case "Suggest":
                    OpenSuggestion();
                    break;
                case "Accuse":
                    OpenAccusation();
                    break;
                default:
                    throw new Exception("You shitty fuck, how did you get here?!!?  CompleteEndTurnAction - Game");
                    break;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        public void OpenAccusation()
        {
            frmAccuse.Invoke((MethodInvoker)delegate()
            {
                frmAccuse.Show();
            });
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void AccuseSelected(object sender, AccusationEventArgs e)
        {
            networkOps.SendMessage(e.character + ":" + e.room + ":" + e.weapon, me.Character.Name, MessageType.Accuse);
        }


        /// <summary>
        /// 
        /// </summary>
        public void OpenSuggestion()
        {
            frmSuggest.FillCrap(cardDeck, me.CurrentRoom.Name);
            frmSuggest.Show();
            
        }

        private void AccusationResponseDetected(object sender, SuggestionMadeEventArgs e)
        {
            if (me.Character.Name == e.suggester)
            {
                if (e.message.Split(':')[0] == "fail")
                {
                    frmGameOver.SetMessages("Your Accusation Was Incorrect!", e.message.Split(':')[1], e.message.Split(':')[2], e.message.Split(':')[3], e.suggester);
                    me.Active = false;
                    EndTurn();
                    
                    //networkOps.SendMessage("lost", me.Character.Name, MessageType.PlayerLost);
                }
                else
                {
                    frmGameOver.SetMessages("Your Accusation Was Correct!  You Win!", e.message.Split(':')[1], e.message.Split(':')[2], e.message.Split(':')[3], e.suggester);
                }
            }
            else
            {
                if (e.message.Split(':')[0] == "fail")
                {
                    frmGameOver.SetMessages(e.suggester + "'s Accusation Was Incorrect!", e.message.Split(':')[1], e.message.Split(':')[2], e.message.Split(':')[3], e.suggester);
                    players.GetPlayer(e.suggester).Active = false;
                }
                else
                {
                    frmGameOver.SetMessages(e.suggester + "'s Accusation Was Correct!  You Lost!", e.message.Split(':')[1], e.message.Split(':')[2], e.message.Split(':')[3], e.suggester);
                }
            }

            Thread.Sleep(200);
            RenderPlayers();
            frmGameOver.Invoke((MethodInvoker)delegate()
            {
                frmGameOver.Show();
            });
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PlayerLost(object sender, SuggestionMadeEventArgs e)
        {
            foreach (Player p in players.Players)
            {
                if (p.Character.Name == e.suggester)
                {
                    p.Active = false;
                }
            }
        }

        
    }
}
